﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;

using Microsoft.WindowsAPICodePack.Shell;

namespace My_TV_Manager.Model
{
    [Serializable()]

    public class Series : ISerializable, IComparable
    {
        #region Private Data

        private string name;
        private List<Season> seasons;
        private DateTime lastModified;

        private bool directorySeries;

        private int tvdbId;
        private string imdbId;
        private int runtime;
        private string status;

        #endregion

        #region Properties

        public String Name { get { return name; } }
        public List<Season> Seasons { get { return seasons; } }
        public int Size { get { return seasons.Count; } }
        public DateTime LastModified { get { return lastModified; } }
        public bool IsDirectorySeries { get { return directorySeries; } }
        public int TVDBID { get { return tvdbId; } }
        public string IMDBID { get { return imdbId; } }
        public int Runtime { get { return runtime; } }
        public string Status { get { return status; } }

        #endregion

        #region Constructors

        // Default constructor for tests
        public Series()
        {
            name = "";
            seasons = new List<Season>();
            lastModified = DateTime.Now;
            directorySeries = false;
            tvdbId = 0;
            imdbId = "";
            runtime = 0;
            status = "";
        }

        // Constructor to create a new series
        public Series(string name, List<Season> seasons)
        {
            this.name = name;
            this.seasons = seasons;
            lastModified = DateTime.Now;
            directorySeries = false;
            tvdbId = -1;
            imdbId = "";
            runtime = 0;
            status = "";
        }

        // Constructor to create a new series at a specific time
        public Series(string name, List<Season> seasons, DateTime lastModified)
        {
            this.name = name;
            this.seasons = seasons;
            this.lastModified = lastModified;
            directorySeries = false;
            tvdbId = -1;
            imdbId = "";
            runtime = 0;
            status = "";
        }

        // Constructor which parses a directory
        public Series(string path)
        {
            LoadFromDirectory(path);
        }

        // Constructor which parses an XML document
        public Series(XmlDocument xmlDocument)
        {
            name = Conversion.SeriesTVDBToLocal(xmlDocument.GetElementsByTagName("SeriesName")[0].InnerText);

            seasons = new List<Season>();
            XmlNodeList episodes = xmlDocument.GetElementsByTagName("Episode");
            foreach (XmlNode node in episodes)
            {
                Episode e = new Episode(name, node);
                Add(e);
            }

            directorySeries = false;

            int.TryParse(xmlDocument.GetElementsByTagName("id")[0].InnerText, out tvdbId);
            imdbId = xmlDocument.GetElementsByTagName("IMDB_ID")[0].InnerText;
            int.TryParse(xmlDocument.GetElementsByTagName("Runtime")[0].InnerText, out runtime);
            status = xmlDocument.GetElementsByTagName("Status")[0].InnerText;

            lastModified = DateTime.Now;

            Print();
        }

        #endregion

        #region Functions

        // Recursive print
        public void Print()
        {
            Console.Out.WriteLine(name);
            foreach (Season season in seasons)
            {
                season.Print();
            }
        }

        // Add a season to the series
        public bool Add(Season season)
        {
            // Season 0 is useless to us
            if (season.Number <= 0) { return false; }

            seasons.Add(season);
            seasons.Sort();
            return true;
        }

        // Add an episode to the correct season
        private bool Add(Episode episode)
        {
            // Season 0 is useless to us
            if (episode.SeasonNumber == 0 ||
                episode.Title == "")
            {
                return false;
            }

            // Potentially ignore episodes from the future
            if (!Settings.IncludeFutureEpisodes &&
                episode.FirstAired > DateTime.Now)
            {
                return false;
            }

            // Search for the correct season
            foreach (Season season in seasons)
            {
                if (season.Number == episode.SeasonNumber)
                {
                    season.Add(episode);
                    return true;
                }
            }

            // If the correct season does not exist, create it
            seasons.Add(new Season(episode.SeasonNumber, new List<Episode>()));
            seasons[seasons.Count - 1].Add(episode);
            seasons.Sort();
            return true;
        }

        // Load series from the directory path
        public bool LoadFromDirectory(string path)
        {
            // If the directory doesn't exist, return
            if (Directory.Exists(path)) { return false; }

            // Parse the path to find the series name
            name = path.Substring(path.LastIndexOf('\\') + 1);

            // Load series data from the directory
            lastModified = Directory.GetLastWriteTime(path);
            string[] dir = Directory.GetDirectories(path);

            // Load season data from the subdirectories
            seasons = new List<Season>();
            foreach (string d in dir)
            {
                Season s = new Season(d);
                seasons.Add(s);

                // Get the real lastModified time
                if (s.LastModified > lastModified)
                {
                    lastModified = s.LastModified;
                }
            }
            seasons.Sort();

            directorySeries = true;
            return true;
        }

        #endregion

        #region ISerializable Members

        // Deserialization constructor
        public Series(SerializationInfo info, StreamingContext context)
        {
            name = info.GetString("Name");
            seasons = (List<Season>)info.GetValue("Seasons", typeof(List<Season>));
            lastModified = info.GetDateTime("LastModified");
            directorySeries = info.GetBoolean("IsDirectorySeries");
            tvdbId = info.GetInt32("TVDBID");
            imdbId = info.GetString("IMDBID");
            runtime = info.GetInt32("Runtime");
            status = info.GetString("Status");
        }

        // Implemented for serialization
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Name", name);
            info.AddValue("Seasons", seasons, typeof(List<Season>));
            info.AddValue("LastModified", lastModified);
            info.AddValue("IsDirectorySeries", directorySeries);
            info.AddValue("TVDBID", tvdbId);
            info.AddValue("IMDBID", imdbId);
            info.AddValue("Runtime", runtime);
            info.AddValue("Status", status);
        }

        #endregion

        #region IComparable Members

        public int CompareTo(object obj)
        {
            if (obj is Series)
            {
                Series s = (Series)obj;
                return name.CompareTo(s.Name);
            }
            else
            {
                throw new ArgumentException("Object is not of type Series");
            }
        }

        #endregion
    }
}
